package simpleWave;

import java.util.ArrayList;

import cli.ArgHandler;
import cli.Option;
import data.PersonData;
import data.Stadium;
import display.Display;


public class Main {
	
	private Option numPersonsOption;
	private Option waveStartIndexOption;
	private Option randomlyStartWavesOption;
	private Synchronizer synchronizer = new Synchronizer();
	
	private ArrayList<PersonProcess> persons;
	private ArrayList<Stadium> history;
	
	private ArrayList<String> lines;
	

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new Main(args);
	}
	
	public Main(String[] args) {
		
		handleInput(args);
		
		
		this.lines = new ArrayList<String>();
		this.history = new ArrayList<Stadium>();
		this.persons = generatePersons(history);
		
		// Add Listeners
		this.synchronizer.addSynchronizedListener(new HistoryListener());
		
		// Create display and let it add listener
		Display display = new Display(persons, synchronizer);
		display.setLines(lines);
		
		startPersons(persons);
		//display.start();
		
	}

	/**
	 * Handle command line input and create options, using default values
	 * @param args
	 * @return application options
	 */
	private void handleInput(String[] args) {
		
		ArgHandler argHandler = new ArgHandler(args);
		ArrayList<Option> options = new ArrayList<Option>();
		
		numPersonsOption = new Option(1, "-n");
		numPersonsOption.createDefaultOptions("9");
		options.add(numPersonsOption);
		
		waveStartIndexOption = new Option(1, "-i");
		//waveStartIndexOption.createDefaultOptions("4");
		options.add(waveStartIndexOption);
		
		randomlyStartWavesOption = new Option(0, "-r");
		options.add(randomlyStartWavesOption);
		
		argHandler.handleArgs(options);
		
		
		/*
		 * Add method for  
		 */
		
		if (waveStartIndexOption.getOptionArgs() == null &&
				!randomlyStartWavesOption.isExists()) {
			
			System.out.println("You need to specify a wave index or randomly create waves");
			System.out.println();
			
			System.out.println("Name:");
			System.out.println("EmergetWave");
			System.out.println();
			
			System.out.println("Synopsis:");
			System.out.print("[-n number]");
			System.out.print("[-i index]");
			System.out.print("[-r]");
			System.out.println();
			System.out.println();
			
			System.out.println("Description:");
			System.out.println("-n number of persons in wave");
			System.out.println("-i schedules a wave to start at [index]");
			System.out.println("-r randomly creates waves");
			System.out.println();
			System.exit(-1);
		}
	}

	public Option getNumPersonsOption() {
		return numPersonsOption;
	}

	/**
	 * Start given Persons threads
	 * @param persons
	 */
	private void startPersons(ArrayList<PersonProcess> persons) {
		for (PersonProcess person : persons) {
			person.start();
		}
	}

	private ArrayList<PersonProcess> generatePersons(ArrayList<Stadium> history) {
		// Genereate persons and put them in list
		ArrayList<PersonProcess> persons = new ArrayList<PersonProcess>();
		generatePerson(persons, history,
				Integer.parseInt(numPersonsOption.getOptionArgs()[0]), 
				randomlyStartWavesOption.isExists());
		
		// Set wave starter if given
		if(waveStartIndexOption.getOptionArgs() != null) {
			persons.get(Integer.parseInt(waveStartIndexOption.getOptionArgs()[0])).setWaveStarter(true);
		}

		synchronizer.setNumThreads(persons.size());
		
		return persons;
	}

	/**
	 * Dynamically create persons objects and set attributes
	 * @param persons
	 * @param numPersons
	 * @param previousPerson
	 * @return persons
	 */
	private ArrayList<PersonProcess> generatePerson(ArrayList<PersonProcess> persons, 
			ArrayList<Stadium> history, int numPersons, 
			boolean randomlyStartWave) {
		
		// Basecase
		if (numPersons == 0) {
			return persons;
		}
		
		// Create Person and add to list
		PersonProcess person = new PersonProcess(synchronizer);
		PersonData personData = new PersonData(persons.size(), PersonData.PersonState.SITTING);
		person.setPersonData(personData);
		person.setRandomlyStartingWaves(randomlyStartWave);
		person.setHistory(history);
		person.setLines(lines);
		person.setPersons(persons);
		
		persons.add(person);
		
		// Iterate
		generatePerson(persons, history, numPersons-1, randomlyStartWave);
		
		return persons;
	}
	
	private class HistoryListener implements SynchronizedListener {

		@Override
		public void synchronizationPerformed() {
			
			// Take a snapshot of the stadium, this means:
			// Extract all person data from person threads and store them in history
			Stadium stadium = new Stadium();
			ArrayList<PersonData> row = new ArrayList<PersonData>();
			for (PersonProcess person : persons) {
				
				// Clones the person data from person process and add it to row
				 try {
					row.add((PersonData) person.getPersonData().Clone());
				} catch (CloneNotSupportedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			stadium.setRow(row);
			
			history.add(stadium);
			
		}
	}
}
